package com.macro.mall.demo.tanent;

import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.DefaultReflectorFactory;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.sql.Connection;


/**
 * mybatis 拦截器 动态拼接 租户id
 */
@Slf4j
@Intercepts({
        @Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})
})
public class TenantInterceptor implements Interceptor {
    public static final String TENANT_ID = "tenant_id";
    public static final String WHERE = "where";
    public static final String FROM = "from";
    public static final String FAKE_TENANT_ID = "'string'";

    @Override
    public Object intercept(Invocation invocation) throws Throwable {
        StatementHandler statementHandler = (StatementHandler) invocation.getTarget();

        MetaObject metaObject = MetaObject.forObject(statementHandler, SystemMetaObject.DEFAULT_OBJECT_FACTORY,
                SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY, new DefaultReflectorFactory());

        // org.apache.ibatis.session.Configuration.newStatementHandler
        MappedStatement mappedStatement = (MappedStatement) metaObject.getValue("delegate.mappedStatement");

        // id为执行的mapper方法的全路径名，如com.gitee.shigen.mapper.UserMapper.selectByPrimaryKey
        String id = mappedStatement.getId();
        // sql语句类型 select、delete、insert、update
        SqlCommandType sqlCommandType = mappedStatement.getSqlCommandType();
        BoundSql boundSql = statementHandler.getBoundSql();
        // 获取到原始sql语句 带？号
        String sql = boundSql.getSql().toLowerCase();
        // 注解逻辑判断  添加注解了才拦截
        // 如：cn.hy.biz.mapper.UserMapper
        Class<?> classType = Class.forName(mappedStatement.getId().substring(0, mappedStatement.getId().lastIndexOf(".")));
        // selectByPrimaryKey
        String mName = mappedStatement.getId().substring(mappedStatement.getId().lastIndexOf(".") + 1);
        for (Method method : classType.getDeclaredMethods()) {
            // mapper 接口上取 tenant 注解
            if (method.isAnnotationPresent(Tenant.class) && mName.equals(method.getName())) {
                Tenant tenantId = method.getAnnotation(Tenant.class);
                if (tenantId.flag() && sqlCommandType.equals(SqlCommandType.SELECT)) {
                    StringBuilder sb = new StringBuilder(sql);
                    if (sql.contains(WHERE)) {
                        int whereIndexOf = sql.indexOf(WHERE);
                        sb.insert(whereIndexOf + WHERE.length(), " " + TENANT_ID + "=" + FAKE_TENANT_ID + " and ");
                    } else {
                        // 不存在where
                        int tableNameAfterIndex = getTableNameAfterIndex(sql);
                        if (tableNameAfterIndex != -1) {
                            sb.insert(getTableNameAfterIndex(sql) + 1, WHERE + " " + TENANT_ID + "=" + FAKE_TENANT_ID + " ");
                        } else {
                            //没有则直接拼接最后
                            sb.append(" " + WHERE + " " + TENANT_ID + "=" + FAKE_TENANT_ID + " ");
                        }
                    }
                    sql = sb.toString();
                }
            }
        }
        // 通过反射修改sql语句
        Field field = boundSql.getClass().getDeclaredField("sql");
        field.setAccessible(true);
        field.set(boundSql, sql);
        return invocation.proceed();
    }

    private int getTableNameAfterIndex(String sql) {
        int fromIndex = sql.indexOf(FROM);
        return sql.indexOf(" ", fromIndex + 5);
    }

    public static void main(String[] args) {
        String sql = "select * from user ";
        StringBuffer sb = new StringBuffer(sql);
        TenantInterceptor tenantInterceptor = new TenantInterceptor();
        int tableNameAfterIndex = tenantInterceptor.getTableNameAfterIndex(sql);
        System.out.println("tableNameAfterIndex = " + tableNameAfterIndex);
        sb.insert(tableNameAfterIndex + 1, WHERE + " " + TENANT_ID + "=" + FAKE_TENANT_ID + " ");
        System.out.println("sb = " + sb);
    }
}
